commonlibsse_ng\re\b/
BSStringPool.rs

1use crate::re::BSAtomic::BSSpinLock;
2use core::{
3    ffi::c_char,
4    sync::atomic::{AtomicU16, Ordering},
5};
6
7#[derive(Debug)]
8#[repr(C)]
9pub struct Entry<T: StringFormat> {
10    /// Pointer to the previous entry in the list.
11    pub(crate) left: *mut Entry<T>,
12    /// Flags for entry status and reference count.
13    pub(crate) flags: AtomicU16,
14    /// CRC checksum for the entry.
15    pub(crate) crc: u16,
16    /// Union that holds either length or a pointer to the next entry.
17    pub(crate) length_or_right: LengthOrRight<T>,
18}
19const _: () = {
20    assert!(core::mem::offset_of!(Entry<U8>, left) == 0x0);
21    assert!(core::mem::offset_of!(Entry<U8>, flags) == 0x8);
22    assert!(core::mem::offset_of!(Entry<U8>, crc) == 0xa);
23    assert!(core::mem::offset_of!(Entry<U8>, length_or_right) == 0x10);
24    assert!(core::mem::size_of::<Entry<U8>>() == 0x18);
25};
26
27/// A union representing either the length of the entry or a pointer to the next entry.
28/// This allows for flexible storage of either data (length) or a pointer (right).
29#[repr(C)]
30pub union LengthOrRight<T: StringFormat> {
31    /// Holds the length of the entry.
32    length: u32,
33    /// Holds a pointer to the next entry.
34    right: *mut Entry<T>,
35}
36const _: [(); core::mem::size_of::<LengthOrRight<U8>>()] = [(); 0x8];
37
38impl<T> core::fmt::Debug for LengthOrRight<T>
39where
40    T: StringFormat,
41{
42    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43        unsafe {
44            f.debug_struct("LengthOrRight")
45                .field("length", &self.length)
46                .field("right", &self.right)
47                .finish()
48        }
49    }
50}
51
52impl<T: StringFormat> Entry<T> {
53    /// Constant to represent a "wide" entry. This is used for determining if the entry is wide.
54    pub const WIDE: u16 = 1 << 15;
55
56    /// Mask used for reference count in flags.
57    pub const REF_COUNT_MASK: u16 = 0x7FFF;
58
59    /// Mask used to extract the length value from the `length_or_right` union.
60    pub const LENGTH_MASK: u32 = 0xFFFFFF;
61
62    /// Acquire the entry by incrementing its reference count.
63    ///
64    /// This method ensures atomicity by loading the current value of the reference count, and then
65    /// attempts to increment the reference count. The operation is performed in a loop to ensure
66    /// that the reference count does not overflow.
67    pub fn acquire(&self) {
68        let flags = &self.flags;
69        let mut expected;
70        loop {
71            expected = flags.load(Ordering::Relaxed);
72            if (expected & Self::REF_COUNT_MASK) >= Self::REF_COUNT_MASK {
73                break;
74            }
75            if flags
76                .compare_exchange_weak(
77                    expected,
78                    expected.wrapping_add(1),
79                    Ordering::AcqRel,
80                    Ordering::Relaxed,
81                )
82                .is_ok()
83            {
84                break;
85            }
86        }
87    }
88
89    /// Get the CRC checksum of the entry.
90    #[inline]
91    pub const fn crc(&self) -> u16 {
92        self.crc
93    }
94
95    /// Returns the length of the entry, extracting it from the `length_or_right` union.
96    ///
97    /// This corresponds to the `length` or `size` function in the C++ API.
98    ///
99    /// # Returns
100    ///
101    /// Returns the length stored in the `length_or_right` union after applying the `LENGTH_MASK`.
102    #[allow(clippy::len_without_is_empty)]
103    #[inline]
104    pub const fn len(&self) -> u32 {
105        unsafe { self.length_or_right.length & Self::LENGTH_MASK }
106    }
107
108    /// Returns a raw pointer to the entry's data.
109    ///
110    /// This corresponds to the `data` function in the C++ API.
111    #[inline]
112    pub fn as_raw(&self) -> *const T::Unit {
113        T::is_valid(self.is_wide());
114        unsafe { (self as *const Self).add(1).cast::<T::Unit>() }
115    }
116
117    /// Releases the entry and performs the necessary cleanup. This function is unsafe because it
118    /// involves dereferencing raw pointers.
119    ///
120    /// # Safety
121    /// - The caller must ensure that the entry is properly allocated and not already released.
122    /// - Not having a double free.
123    #[inline]
124    pub unsafe fn release(entry: &*const T::Unit) {
125        unsafe { T::release(entry) };
126    }
127
128    /// Checks whether the entry is "wide" based on the flags.
129    #[inline]
130    fn is_wide(&self) -> bool {
131        (self.flags.load(Ordering::Relaxed) & Self::WIDE) != 0
132    }
133}
134
135/// The `StringFormat` trait defines the methods required to interact with a specific string format type.
136///
137/// This includes the type of unit (e.g., `u8` for `c_char` or `u16` for `w_char_t`) and methods for validation
138/// and releasing memory associated with entries.
139pub trait StringFormat {
140    /// U8/U16
141    type Unit;
142
143    /// Releases the entry associated with the string format type.
144    ///
145    /// # Safety
146    ///
147    /// - The caller must ensure the entry is valid and allocated.
148    /// - Not having a double free.
149    unsafe fn release(entry: &*const Self::Unit);
150
151    /// Validates the entry, ensuring the proper string format (e.g., UTF-8 or UTF-16).
152    ///
153    /// # Panics
154    ///
155    /// If the format does not match the expected type (wide or not), this function will panic.
156    fn is_valid(is_wide: bool);
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
160pub enum U8 {}
161
162impl StringFormat for U8 {
163    type Unit = c_char;
164
165    #[inline]
166    unsafe fn release(entry: &*const Self::Unit) {
167        unsafe { release8(entry) }
168    }
169
170    #[inline]
171    fn is_valid(is_wide: bool) {
172        assert!(!is_wide);
173    }
174}
175
176/// The `wchar_t` type is an implementation-defined wide character type.
177///
178/// In Microsoft compilers, it represents a 16-bit wide character used to store Unicode encoded as UTF-16LE.
179/// - ref: [`char、wchar_t、char8_t、char16_t、char32_t`](https://learn.microsoft.com/cpp/cpp/char-wchar-t-char16-t-char32-t?view=msvc-170)
180#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
181pub enum U16 {}
182
183impl StringFormat for U16 {
184    type Unit = u16;
185
186    #[inline]
187    unsafe fn release(entry: &*const Self::Unit) {
188        unsafe { release16(entry) }
189    }
190
191    #[inline]
192    fn is_valid(is_wide: bool) {
193        assert!(is_wide);
194    }
195}
196
197/// Releases a `c_char` entry. This is marked as `unsafe` because it operates directly on raw pointers.
198#[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67847, ae_id = 69192)]
199pub unsafe fn release8(entry: &*const c_char) {}
200
201/// Releases a `wchar_t` entry. This is marked as `unsafe` because it operates directly on raw pointers.
202#[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67848, ae_id = 69193)]
203pub unsafe fn release16(entry: &*const u16) {}
204
205#[repr(C)]
206pub struct BucketTable {
207    buckets: [*mut Entry<U8>; 0x10000], // 00000 - index using hash & kEntryIndexMask
208    locks: [BSSpinLock; 0x10000 / 0x800], // 80000 - index using hash & kLockIndexMask
209    initialized: bool,                  // 80100
210}
211const _: [(); core::mem::size_of::<BucketTable>()] = [(); 0x80108];
212
213impl BucketTable {
214    /// Returns the singleton instance of the `BucketTable`.
215    ///
216    /// This function is used to get a global instance for the table.
217    #[allow(clippy::use_self)]
218    #[commonlibsse_ng_derive_internal::relocate_fn(se_id = 67855, ae_id = 69200)]
219    pub fn get_singleton() -> *mut BucketTable {}
220}